package com.gitee.wsl.compose.state.enqueue

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.gitee.wsl.base.RemovalReason
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlin.time.Duration

interface EnqueueModel{
    val duration: Duration
}

interface EnqueueState<Model:EnqueueModel> {
    /**
     * The maximum number of nudges that can be displayed at once.
     */
     val maxStack: Int

    /**
     * The [CoroutineScope] used for managing nudge-related coroutines.
     */
     val coroutineScope: CoroutineScope

    // Holds the current list of NudgeModel instances being displayed
    var currentNudgeModels:List<Model>

    // Flags to track when a new nudge is being displayed or removed for animations
    val isNewNudgeDisplayed:  MutableStateFlow<Boolean>
    val isNudgeRemoved:  MutableStateFlow<Boolean>

}

/**
 * Displays a new nudge with the provided [NudgeModel].
 *
 * @param model The [NudgeModel] to be displayed.
 */
fun <Model: EnqueueModel> EnqueueState<Model>.display(model: Model) {
    isNewNudgeDisplayed.value = false
    isNudgeRemoved.value = false

    // Add the new nudge to the current list
    currentNudgeModels = currentNudgeModels.updateNudgeModels {
        add(model)
    }

    coroutineScope.launch {
        // Simulate delay for animation or processing
        delay(100)
        isNewNudgeDisplayed.value = true

        // Simulate delay for displaying the nudge
        delay(400)

        // Remove the oldest nudge if the stack size exceeds maxStack
        currentNudgeModels = currentNudgeModels.updateNudgeModels {
            if (size > maxStack) removeFirstOrNull()
        }
        isNudgeRemoved.value = true

        // Final delay before completing the update
        delay(100)
        isNudgeRemoved.value = false
    }
}

suspend fun <Model: EnqueueModel> EnqueueState<Model>.handleNudgeDisplay() {
    val data = currentNudgeModels
    data.lastOrNull()?.let { currentNudge ->
        delay(currentNudge.duration)
        updateNudgeState { nudgeList ->
            nudgeList.remove(currentNudge)
        }
    }
}

fun <Model: EnqueueModel> EnqueueState<Model>.handleNudgeRemoval(reason: RemovalReason) {
    coroutineScope.launch {
        updateNudgeState(
            immediateUpdate = reason == RemovalReason.UserAction,
        ) { nudgeList ->
            nudgeList.removeLastOrNull()
        }
    }
}

fun <Model: EnqueueModel> EnqueueState<Model>.updateNudgeState(
    immediateUpdate: Boolean = false,
    update: (MutableList<Model>) -> Unit,
) {
    isNewNudgeDisplayed.value = false
    coroutineScope.launch {
        if (!immediateUpdate) {
            delay(500)
        }
        currentNudgeModels = currentNudgeModels.toMutableList().apply(update)
        isNewNudgeDisplayed.value = true
    }
}


/**
 * Implementation of [NudgeState] that manages the display and lifecycle of nudges.
 *
 * @param maxStack The maximum number of nudges that can be displayed at once.
 * @param animation The animation to use for displaying nudges.
 * @param placement The placement of the nudges on the screen.
 * @param coroutineScope The [CoroutineScope] used for managing nudge-related coroutines.
 */
@Stable
open class EnqueueStateImpl<Model: EnqueueModel>(
    override val maxStack: Int,
    override val coroutineScope: CoroutineScope
) : EnqueueState<Model> {
    // Holds the current list of NudgeModel instances being displayed
    override var currentNudgeModels by mutableStateOf<List<Model>>(emptyList())

    // Flags to track when a new nudge is being displayed or removed for animations
    override val isNewNudgeDisplayed = MutableStateFlow(false)
    override val isNudgeRemoved = MutableStateFlow(false)
}

private fun<Model: EnqueueModel> List<Model>.updateNudgeModels(
    action: MutableList<Model>.() -> Unit,
) = this.toMutableList().apply(action)


/**
 * Validates the current [NudgeState] to ensure that it is correctly configured.
 *
 * Throws an [InvalidNudgeStateException] if the configuration is invalid.
 */

fun EnqueueState<*>.requireValidState() {
    require(maxStack > 0) {
        throw InvalidNudgeStateException("maxStack must be greater than 0")
    }
}

@Composable
fun <Model: EnqueueModel , MState: EnqueueState<Model>> MState.Render(
    modifier: Modifier,
    content: @Composable MState.(modifier: Modifier,newHosted: Boolean,removeHosted: Boolean) -> Unit
) {
    val newNudgeHosted by isNewNudgeDisplayed.collectAsState()
    val removedNudgeHosted by isNudgeRemoved.collectAsState()

    LaunchedEffect(currentNudgeModels) {
        handleNudgeDisplay()
    }

    if (currentNudgeModels.isNotEmpty()) {
        content(modifier,newNudgeHosted,removedNudgeHosted)
    }
}


private class InvalidNudgeStateException(message: String) : Exception(message)

/**
 * Provides the current [NudgeState] to the composition.
 *
 * @see ProvideNudgeState
 */
//internal val  LocalNudgeState: ProvidableCompositionLocal<EnqueueState<EnqueueModel>> = staticCompositionLocalOf {
//    error("LocalNudgeState is not provided")
//}

/**
 * Provides a [NudgeState] to the Compose hierarchy.
 *
 * @param state The [NudgeState] to provide.
 * @param content The content that will have access to the provided [NudgeState].
 */
//@Composable
//fun <Model: EnqueueModel> ProvideNudgeState(
//    state: EnqueueState<Model>,
//    content: @Composable () -> Unit,
//) {
//    CompositionLocalProvider(LocalNudgeState provides state) {
//        content()
//    }
//}
